﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Net;
using System.Threading;

namespace PARCS.Core
{
    [Serializable]
    public class HostInfo
    {
        public string Name { get; private set; }
        public IPAddress Address { get; private set; }
        //public boolean respond = false;

        public Socket Socket { 
            get { return _socket; } 
            set { _socket = value; }
        }

        //public int connectionsNumber = 0;
        //public int pointsNumber = 0;

        [NonSerialized]
        private Socket _socket;

        protected HostInfo() { }

        public HostInfo(String name)
        {
            Name = name;
        }
        public HostInfo(IPAddress ip)
        {
            Address = ip;
        }
        public HostInfo(string name, IPAddress ip)
        {
            Name = name;
            Address = ip;
        }

        public HostInfo GetCopy()
        {
            return new HostInfo(Name, Address);
        }

        public bool ResolveName()
        {
            if (Address != null) return true;

            try
            {
                Address = Dns.GetHostAddresses(Name)[0];
                return true;
            }
            catch
            {
                return false;
            }
        }

        public bool Connect()
        {
            return Connect(false, Const.DaemonTCPPort);
        }
        public bool Connect(int port)
        {
            return Connect(false, port);
        }
        public bool Connect(bool breakconn)
        {
            return Connect(breakconn, Const.DaemonTCPPort);
        }
        
        /// <summary>
        /// Connect to the host
        /// </summary>
        /// <param name="breakconn">If true - disconnect after connecting</param>
        /// <param name="port">Remote TCP port</param>
        /// <returns>True - if connected</returns>
        public bool Connect(bool breakconn, int port)
        {
            if (IsConnected())
            {
                if (_socket == null) Console.Error.WriteLine("cycleConnect: error: socket == null");
                return true;
            }

            _socket = null;

            try
            {
                if (!ResolveName()) return false;

                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                if (_socket == null) return false;

                if (breakconn)
                {
                    _socket.Close();
                    _socket = null;
                }
            }
            catch (IOException)
            {
                return false;
            }

            if (_socket == null) Console.Error.WriteLine("cycleConnect: error: socket==null");

            return true;
        }

        public bool CycleConnect()
        {
            return CycleConnect(Const.DaemonTCPPort);
        }

        public bool CycleConnect(int port)
        {
            if (!ResolveName()) return false;

            for (var i = 0; i < 10; i++)
            {
                if (Connect(false, port))
                {
                    if (_socket == null) Console.Error.WriteLine("cycleConnect: error: socket==null");
                    return true;
                }

                Thread.Sleep(400);
            }
            return false;
        }

        public override string ToString()
        {
            return Name ?? (Address != null ? Address.ToString() : string.Empty);
        }

        public static void CloseSocket(Socket socket)
        {
            if (socket != null)
                try
                {
                    socket.Close();
                }
                catch (Exception e)
                {
                    Console.Error.WriteLine("closeSocket: " + e.Message);
                }
        }
        public void CloseSocket()
        {
            if (_socket == null) return;

            CloseSocket(_socket);
            _socket = null;
        }
        public static HostInfo GetLocalHost()
        {
            try
            {
                return new HostInfo(Dns.GetHostAddresses(Dns.GetHostName())[0]);
            }
            catch
            {
                return new HostInfo("localhost");
            }
        }

        public bool IsConnected()
        {
            return (_socket != null) && (!IsClosed(_socket)) && (_socket.Connected);
        }

        public bool IsClosed(Socket s)
        {
            return !(s.Poll(1000, SelectMode.SelectRead) & s.Available == 0);
        }
    }
}
